//Second revision: lightseeking studio
package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2/log"

	"github.com/cloudlink-omega/accounts"
	"github.com/cloudlink-omega/accounts/pkg/structs"
	"github.com/cloudlink-omega/backend/pkg/server"
	"github.com/cloudlink-omega/signaling"
	"github.com/cloudlink-omega/storage/pkg/common"
	"github.com/cloudlink-omega/storage/pkg/types"
	"github.com/gofiber/fiber/v2"

	"github.com/gofiber/fiber/v2/middleware/cors"
	fiber_logger "github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/monitor"
	"github.com/gofiber/fiber/v2/middleware/recover"
	"github.com/joho/godotenv"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	gorm_logger "gorm.io/gorm/logger"
)

func main() {
	// Initialize variables for flags and environment
	var err error
	var email_port int
	var https_mode, use_email, turn_only, enforce_https, enable_google, enable_github, enable_discord bool

	// Load environment variables
	if err := godotenv.Load(); err != nil {
		panic(err)
	}

	// Read flags from environment
	https_mode = os.Getenv("HTTPS_MODE") == "true"
	use_email = os.Getenv("USE_EMAIL") == "true"
	turn_only = os.Getenv("TURN_ONLY") == "true"
	enforce_https = os.Getenv("ENFORCE_HTTPS") == "true"
	enable_google = os.Getenv("ENABLE_GOOGLE") == "true"
	enable_github = os.Getenv("ENABLE_GITHUB") == "true"
	enable_discord = os.Getenv("ENABLE_DISCORD") == "true"
	convert_old_db := os.Getenv("CONVERT_OLD_DB") == "true"
	bypass_db := os.Getenv("BYPASS_DB") == "true"

	// Initialize database
	if bypass_db {
		log.Info("You might see an error about being unable to connect. Please ignore it.")
	}
	db, err := gorm.Open(mysql.Open(
		fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=True",
			os.Getenv("DB_USERNAME"),
			os.Getenv("DB_PASSWORD"),
			os.Getenv("DB_HOST"),
			os.Getenv("DB_PORT"),
			os.Getenv("DB_DATABASE"),
		)), &gorm.Config{
		Logger: gorm_logger.Default.LogMode(gorm_logger.Info),
	})
	if err != nil && !bypass_db {
		panic(err)
	}

	// Init global cache
	cache := types.NewDBCache()

	// Compile authorized domains for CORS（支持空、true、逗号/空格/分号分隔）
	envAllowed := strings.TrimSpace(os.Getenv("ALLOWED_DOMAINS"))
	var allowed_domains string
	var allowedDomainsSlice []string
	if envAllowed == "true" {
		allowed_domains = "*"
		allowedDomainsSlice = []string{"*"}
	} else if envAllowed == "" {
		// 禁止任何域名连接
		allowed_domains = ""
		allowedDomainsSlice = []string{}
	} else {
		parts := strings.FieldsFunc(envAllowed, func(r rune) bool { return r == ',' || r == ' ' || r == ';' })
		cleaned := make([]string, 0, len(parts))
		for _, p := range parts {
			p = strings.TrimSpace(p)
			if p != "" {
				cleaned = append(cleaned, p)
			}
		}
		if len(cleaned) == 0 {
			allowed_domains = ""
			allowedDomainsSlice = []string{}
		} else {
			allowed_domains = strings.Join(cleaned, ",")
			allowedDomainsSlice = cleaned
		}
	}

	// Read port from environment
	if use_email {
		email_port, err = strconv.Atoi(os.Getenv("EMAIL_PORT"))
		if err != nil {
			panic(err)
		}
	}

	// Setup email settings
	mail_config := &structs.MailConfig{
		Enabled:  use_email,
		Port:     email_port,
		Server:   os.Getenv("EMAIL_SERVER"),
		Username: os.Getenv("EMAIL_USERNAME"),
		Password: os.Getenv("EMAIL_PASSWORD"),
	}

	// Initialize the Accounts server
	auth := accounts.New(
		"/accounts",
		os.Getenv("SERVER_URL"),
		os.Getenv("API_DOMAIN"),
		os.Getenv("API_URL"),
		os.Getenv("SERVER_NAME"),
		os.Getenv("PRIMARY_WEBSITE"),
		os.Getenv("SERVER_SECRET"),
		enforce_https,
		db,
		cache,
		mail_config,
		false, // Enable testing mode - Allows accounts to bypass email registration if they use @localhost
		bypass_db,
		true,  // Defer migrations
	)

	// Initialize the Frontend server
	backend := server.New(
		os.Getenv("SERVER_NAME"),
		os.Getenv("SERVER_URL"),
		os.Getenv("HOSTED_PATH"),
		db,
		cache,
		auth,
		mail_config,
		bypass_db,
	)

	// Initialize the Signaling server（用规范化后的 allowedDomainsSlice）
	signaling_server := signaling.New(
		allowedDomainsSlice,
		turn_only,
		auth.APIv1.Auth,
		db,
		backend.DB,
		bypass_db,
		true, // Defer migrations
	)

	// Passthrough authorization server to the backend server
	backend.Authorization = auth.APIv1.Auth

	// Initialize the OAuth providers
	if enable_discord {
		auth.OAuth.Discord(os.Getenv("DISCORD_KEY"), os.Getenv("DISCORD_SECRET"))
	}
	if enable_google {
		auth.OAuth.Google(os.Getenv("GOOGLE_KEY"), os.Getenv("GOOGLE_SECRET"))
	}
	if enable_github {
		auth.OAuth.GitHub(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"))
	}

	// Initialize overall Fiber app
	app := fiber.New(fiber.Config{
		ErrorHandler: backend.ErrorPage,
		ProxyHeader:  "X-Real-IP",
	})

	// Initialize Fiber middleware
	app.Use(fiber_logger.New())
	app.Use(recover.New())

	// Fiber CORS: AllowCredentials 不能和 AllowOrigins:* 同时使用，否则会 panic
	allowCredentials := true
	if allowed_domains == "*" {
		allowCredentials = false
		log.Warn("[CORS] AllowCredentials is disabled because AllowOrigins is set to *. To support credentials, please configure specific domains.")
	} else if allowed_domains == "" {
		allowCredentials = false
		log.Warn("[CORS] ALLOWED_DOMAINS is empty, all cross-origin requests have been blocked.")
	}
	app.Use(cors.New(cors.Config{
		AllowOrigins:     allowed_domains,
		AllowHeaders:     "Origin, Content-Type, Accept",
		AllowCredentials: allowCredentials,
	}))

	// Mount servers in the Fiber app
	app.Mount("/signaling", signaling_server.App)
	app.Mount("/accounts", auth.App)

	// Do this if we have the DB enabled
	if !bypass_db {
		// Mount the backend
		app.Mount("/", backend.App)

		// Create directories for hosted files
		for _, folder := range []string{"projects_public", "projects_private", "developer_art", "game_art", "thumbnails"} {
			dir := os.Getenv("HOSTED_PATH") + "/" + folder
			if _, err := os.Stat(dir); os.IsNotExist(err) {
				os.Mkdir(dir, 0755)
			}
		}

		static := app.Group("/hosted")
		server.StaticHandler(backend, static, os.Getenv("HOSTED_PATH"))
	}

	// Mount metrics middleware
	app.Get("/metrics", monitor.New())

	// Seed the database
	if bypass_db {
		log.Info(`You have opted to disable the DB functionality.

		Going forward, you are on your own.
		Frontend pages will be disabled, and there will be NO security measures in place.
		End users will be able to login with any username and password, and any game ID will
		be accepted blindly. 
		
		This is NOT recommended for production.
		You have been warned.
		`)

	} else {
		log.Info("Migrating and seeding database...")
		if err := common.MigrateAndSeed(db); err != nil {
			panic(err)
		}

		if convert_old_db {
			log.Info("Preparing to convert old database...")

			// Initialize v0 database
			old_db, err := gorm.Open(mysql.Open(
				fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=True",
					os.Getenv("DB_USERNAME"),
					os.Getenv("DB_PASSWORD"),
					os.Getenv("DB_HOST"),
					os.Getenv("DB_PORT"),
					os.Getenv("OLD_DB"),
				)), &gorm.Config{
				Logger: gorm_logger.Default.LogMode(gorm_logger.Info),
			})
			if err != nil {
				panic(err)
			}
			log.Info("Now converting old database...")
			if err := common.ConvertDatabase(old_db, db, auth.DB); err != nil {
				panic(err)
			}
		}
	}

	// Run the app
	log.Info("Starting server...")
	if https_mode {
		app.ListenTLS(os.Getenv("API_URL"), os.Getenv("HTTPS_CERT"), os.Getenv("HTTPS_KEY"))
	} else {
		app.Listen(os.Getenv("API_URL"))
	}
}
